﻿package com.main
{
	import flash.events.Event;
	import com.config.GalleryConfig;
	import com.tools.XmlLoader;
	import com.tools.XmlParser;
	import com.tools.ImageLoader;
	import com.components.NotificationArea;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import com.notifications.XmlEvent;
	import flash.display.Stage;
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.MouseEvent;
	import fl.transitions.TweenEvent;
	import fl.transitions.Tween;
	import fl.transitions.easing.*;
	import fl.motion.easing.Linear;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import gs.TweenLite;
	import gs.TweenMax;
	
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// CLASSE UTILISÉE POUR CRÉER LA GALERIE
	// en l'occurence c'est également le point d'entrée de notre application (c'est la classe de base du .FLA)
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	public class Gallery extends MovieClip
	{
		// référence à la scène globale
		public static var GLOBAL_STAGE : Stage;
		// utilitaire de chargement de XML
		private var chargeXML : XmlLoader;
		// zone de notifications
		public var zoneDeNotif : NotificationArea;
		// compteur d'images restantes à charger
		private var _imagesToLoad : Number;
		// conteneur global des images de la galerie
		private var _imagesContainer : Sprite;
		// zone de texte à afficher en dessous de l'image lorsqu'elle est affichée en grand
		private var textInfos : TextField;
		// formatage pour la zone de texte
		public var textInfosFormat : TextFormat;
		
		// CONSTRUCTEUR
		function Gallery ( )
		{
			// ici on référence la scène principale
			Gallery.GLOBAL_STAGE = stage;
			
			// on crée la zone de notification et on l'ajoute à la DisplayList
			zoneDeNotif = new NotificationArea; // lorsqu'il n'y a pas de paramètres, les '()' sont facultatifs
			zoneDeNotif.y = GalleryConfig.GALLERY_AREA_HEIGHT;
			addChild( zoneDeNotif );
			
			// on lance la méthode de chargement du XML
			loadData( );
			
			/*-----------------------------
			EXEMPLE HORS-CONTEXTE
			moyen de déclencher dynamiquement
			des fonctions via le nom de la fonction en chaine de caractères :
			exemple avec la méthode 'maFonctionPublique'
			-----------------------------*/
			try
			{
				var s : String = 'maFonctionPe'; // remplacer par 'maFonctionPublique pour que cela fonctionne
				if( this[ s ] is Function )
				{
					trace("ouioui ma belle fonction");
					var f : Function = this[ s ];
					f.apply( this, [" ET j'ajoute même un parametre de test !"] );
					f.call( this, " ET un autre test !!", " ET un autre param, etc..." );
				}
			}
			catch(e:ReferenceError)
			{
				//trace("c pas d'bol");
			}
		}
		
		// méthode d'exemple pour l'appel dynamique à des fonctions
		private function maFonctionPublique ( str : String = null, str2 : String = null ) : void
		{
			trace("c bon je suis bien passé grâce à method.apply" + (( str != null ) ? str : '' ) + (( str2 != null ) ? str2 : '' ) );
		}
		
		// fonction de chargement du XML
		protected function loadData( ) : void
		{
			// on lance le chargement du XML
			chargeXML = new XmlLoader( GalleryConfig.XML_GALLERY_PATH );
			// on écoute le résultat du processus
			chargeXML.addEventListener( XmlEvent.XML_LOADED, chargeOK, false );
			chargeXML.addEventListener( XmlEvent.XML_FAILED, chargeECHEC, false );
		}
		
		// méthode exécutée si le chargement du XML réussi
		protected function chargeOK ( pEvt : XmlEvent ) : void
		{
			afficheNotif( "XML chargé, en train d'analyser..." );
			
			// on fait appel à notre classe utilitaire pour parser le XML (c'est à dire analyser puis extraire les informations)
			var imageInfos : Array = XmlParser.parseXml( pEvt.dataXml );
			
			/*-----------------------------
			lancer le chargement des images
			grâce aux infos récoltées à partir du XML... 
			-------------------------------*/
			if ( imageInfos.length > 0 )
			{
				_imagesContainer = new Sprite;
				_imagesContainer.visible = false;
				addChild( _imagesContainer );
				createGallery( imageInfos );
			}
		}
		
		// méthode qui génère dynamiquement la galerie d'images à partir des informations du XML
		// TODO : implémenter la navigation par pagination des vignettes lorsqu'il y a plus de 24 vignettes
		protected function createGallery ( imageInfos : Array ) : void
		{
			var lng : uint = imageInfos.length;
			// on initialise le compteur d'images restantes à charger
			_imagesToLoad = lng;
			
			// on crée un message d'attente pour signifier à l'utilisateur que les images sont en cours de chargement
			afficheNotif( "galerie de photos en cours de chargement..." );
			
			for ( var i : uint = 0 ; i < lng ; i++ )
			{
				// on calcule le layout des vignettes
				var posX : uint = GalleryConfig.HORIZONTAL_PADDING + ( GalleryConfig.THUMB_WIDTH + GalleryConfig.HORIZONTAL_PADDING ) * ( i % GalleryConfig.NB_IMAGES_BY_ROW );
				var posY : uint = GalleryConfig.VERTICAL_PADDING + ( GalleryConfig.THUMB_HEIGHT + GalleryConfig.VERTICAL_PADDING ) * ( Math.floor( i / GalleryConfig.NB_IMAGES_BY_ROW ) );
				
				trace( "posX : "+ posX + " / posY : "+ posY);
				
				// on charge les images en les disposant correctement (layout) puis en les ajoutant à la DisplayList
				// on ajoute l'écouteur pour qu'au clic on puisse afficher l'image en taille entière
				var image : ImageLoader = new ImageLoader( imageInfos[ i ] );
				image.addEventListener( Event.COMPLETE, displayGalleryOnceLoaded );
				image.addEventListener( MouseEvent.CLICK, displayFullImage );
				image.x 				= posX;
				image.y 				= posY;
				image.alpha 			= 0;
				image.buttonMode 		= true; // permet de faire que nos Sprite se comportent comme des Button
				image.mouseEnabled 		= true; // permet de faire que nos Sprite se comportent comme des Button
				image.mouseChildren 	= true; // permet de faire que nos Sprite se comportent comme des Button
				_imagesContainer.addChild( image );
				
				TweenLite.to(
							 	image,
								GalleryConfig.THUMB_TWEEN_DURATION,
								{alpha:1, ease:Regular.easeOut} );
			}
		}
		
		// méthode pour la gestion du chargement des images
		protected function displayGalleryOnceLoaded ( pEvt : Event ) : void
		{
			// si l'Event est bien de type COMPLETE c'est qu'il est a priori diffusé par le ImageLoader et que par conséquent l'image en cours est chargée
			if ( pEvt.type == Event.COMPLETE )
			{
				// on décrémente le compteur
				_imagesToLoad--;
				// on désenregistre l'écouteur auprès de l'image
				pEvt.currentTarget.removeEventListener( Event.COMPLETE, displayGalleryOnceLoaded );
			}
			// s'il ne reste plus d'images à charger
			if ( _imagesToLoad == 0 )
			{
				// on affiche à présent la galerie
				_imagesContainer.visible = true;
				// on diffuse l'Event RENDER pour forcer le rafraîchissement de l'affichage (parfois l'ensemble des photos ne s'affichées pas directement malgré qu'elles étaient bien chargées)
				dispatchEvent( new Event( Event.RENDER ) );
				// on fait en sorte qu'il n'y ai plus de message dans la zone de notifications
				afficheNotif ( ) ;
			}
		}
		
		// méthode pour l'affichage en grand d'une image au clic sur la vignette (+ disparition des vignettes)
		protected function displayFullImage ( pEvt : MouseEvent ) : void
		{
			// on duplique l'image dans un objet Bitmap à partir de la propriété bitmapData de l'image chargée
			var fullImage : Bitmap = new Bitmap( pEvt.currentTarget._imageContainer.getChildAt( 0 ).bitmapData );
			
			// on centre l'image dans la zone d'affichage des images (layout)
			var posX : Number = ( GalleryConfig.STAGE_WIDTH - pEvt.currentTarget.infos.width ) / 2;
			var posY : Number = ( GalleryConfig.GALLERY_AREA_HEIGHT - pEvt.currentTarget.infos.height ) / 2;
			
			fullImage.width = pEvt.currentTarget.infos.width;
			fullImage.height = pEvt.currentTarget.infos.height;
			
			// on crée un écouteur au clic pour repasser en mode affichage des vignettes
			var fullImageContainer : Sprite = new Sprite;
			fullImageContainer.addEventListener( MouseEvent.CLICK, revertToThumbs );
			fullImageContainer.buttonMode = true;
			fullImageContainer.x = posX;
			fullImageContainer.y = posY;
			
			// on crée une zone de texte pour afficher le nom sous l'image
			textInfos = new TextField( );
			textInfos.text = ImageLoader( pEvt.currentTarget ).infos.name;
			textInfosFormat = new TextFormat(
											 	GalleryConfig.IMG_SUBTEXT_FONTTYPE,
												GalleryConfig.IMG_SUBTEXT_FONTSIZE,
												GalleryConfig.IMG_SUBTEXT_FONTCOLOR,
												GalleryConfig.IMG_SUBTEXT_BOLD,
												null,
												null,
												null,
												null,
												GalleryConfig.IMG_SUBTEXT_ALIGNMENT);
			// nous sommes obligés d'utiliser de spécifier cette propriété ainsi que la font à utiliser ('GalleryConfig.IMG_SUBTEXT_FONTTYPE', ci-dessus)
			// pour pouvoir appliquer de la transparence sur le TextField, c'est un bug ou un prérequis de Flash, en tout cas c'est obligatoire
			// DETAILS DU BUG :: http://www.rabidgadfly.com/index.php/2008/10/14/making-alpha-work-on-as3-dynamic-text-fields/
			textInfos.blendMode = GalleryConfig.IMG_SUBTEXT_BLENDMODE;
			textInfos.setTextFormat( textInfosFormat );
			// on fixe la largeur de la zone de texte égale à la scène entière car sinon le texte se retrouve tronqué lorsque le nom de l'image est trop grand
			// on aura également centré l'alignement du texte dans la zone de texte préalablement ('GalleryConfig.IMG_SUBTEXT_ALIGNMENT')
			textInfos.width = GalleryConfig.STAGE_WIDTH;
			textInfos.y = fullImageContainer.y + fullImage.height + GalleryConfig.VERTICAL_PADDING_BETWEEN_IMAGE_AND_SUBTEXT;
			textInfos.x = 0;
			
			var lng : uint = _imagesContainer.numChildren;
			
			// on fait disparaître les vignettes
			// on fait également en sorte qu'on ne puisse plus cliquer sur les vignettes (buttonMode, mouseEnabled, mouseChildren)
			for ( var i : uint = 0 ; i < lng; i++ )
			{
				// on peut également utiliser TweenMax, la bibliothèque associée à TweenLite, pour effectuer des filtres sur les Tweens (ici, un filtre de flou)
				TweenMax.to(
							 	_imagesContainer.getChildAt( i ),
								GalleryConfig.THUMB_TWEEN_DURATION,
								{alpha:.2, ease:Regular.easeIn, blurFilter:{blurX:15}} );
				Sprite( _imagesContainer.getChildAt( i ) ).buttonMode = false;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseEnabled = false;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseChildren = false;
				
				// meilleure alternative si on veut juste faire disparaitre/réapparaitre les vignettes
				/*
				_imagesContainer.getChildAt( i ).visible = false;
				Sprite( _imagesContainer.getChildAt( i ) ).buttonMode = false;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseEnabled = false;
				*/
				
				// autre alternative pour faire a moitié disparaitre puis completement réapparaitre les vignettes
				/*
				_imagesContainer.getChildAt( i ).alpha = .5;
				Sprite( _imagesContainer.getChildAt( i ) ).buttonMode = false;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseEnabled = false;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseChildren = false;
				*/
			}
			
			// on ajoute l'image de type Bitmap à la DisplayList après l'avoir enveloppée dans une instance de type Sprite (pour mieux gérer les interactions souris)
			fullImageContainer.addChild( fullImage );
			_imagesContainer.addChild( fullImageContainer );
			addChild( textInfos );
			
			// on crée un Tween pour faire apparaître l'image avec un effet et on utilise la bibliothèque TweenLite pour aller plus vite et obtenir une animation synchrone
			// TWEENLITE :: http://blog.greensock.com/tweenliteas3/
			fullImage.x = fullImage.width / 2;
			fullImage.scaleX = 0;
			fullImage.alpha = 0;
			textInfos.alpha = 0;
			TweenLite.to(
						 	textInfos,
							GalleryConfig.SUBTEXT_TWEEN_DURATION,
							{alpha:1, ease:Bounce.easeOut} );
			TweenLite.to(
						 	fullImage,
							GalleryConfig.IMG_TWEEN_DURATION,
							{x:0, scaleX:1, alpha:1, ease:Linear.easeNone} );
			
			// ancienne méthode pour créer un Tween pour faire apparaître l'image avec un effet -> il faudrait la paramétrer correctement donc nous préférerons la facilité : utiliser TweenLite !
			/*
			var myTween:Tween = new Tween( fullImageContainer, "scaleX", Linear.easeNone, 0,1, 2, true);
			var myTweenTwo:Tween = new Tween( 
										   fullImage,
										   "x",
										   Linear.easeNone,
										   fullImage.width / 2,
										   0,
										   2,
										   true);
			var myTweenThree:Tween = new Tween( 
											   fullImage,
											   "alpha",
											   Linear.easeNone,
											   0,
											   1,
											   1,
											   true);
			myTween.start();
			myTweenTwo.start();
			myTweenThree.start( );
			*/
		}
		
		// méthode pour enlever de la DisplayList l'élément à la fin de l'effet Tween
		private function removeFullImage ( pContainer : DisplayObject, pTextField : TextField ) : void
		{
			trace( pContainer );
			_imagesContainer.removeChild( pContainer );
			removeChild( pTextField );
		}
		
		// méthode pour le retour à l'affichage sous forme de vignettes
		protected function revertToThumbs ( pEvt : MouseEvent ) : void
		{
			// on récupère la référence à la grande image (de type Bitmap) et à son conteneur (de type Sprite)
			var fullImageContainer : DisplayObjectContainer = _imagesContainer.getChildAt(_imagesContainer.numChildren - 1) as DisplayObjectContainer;
			var fullImage : Bitmap = fullImageContainer.getChildAt( 0 ) as Bitmap;
			trace( "revert ! "+_imagesContainer.numChildren );
			
			// on crée un Tween pour l'effet à la disparition de l'image et on utilise la bibliothèque TweenLite pour aller plus vite et obtenir une animation synchrone
			// TWEENLITE :: http://blog.greensock.com/tweenliteas3/
			fullImage.x = 0;
			fullImage.scaleX = 1;
			fullImage.alpha = 1;
			textInfos.alpha = 1;
			// à noter que 'onComplete' et 'onCompleteParams' est l'équivalent de l'écouteur avec possibilité de passer directement les paramètres, ici les clips à enlever de la DisplayList
			TweenLite.to(
						 	textInfos,
							GalleryConfig.SUBTEXT_TWEEN_DURATION,
							{alpha:0, ease:Bounce.easeOut} );
			TweenLite.to(
						 	fullImage,
							GalleryConfig.IMG_TWEEN_DURATION,
							{x:fullImage.width/2, scaleX:0, alpha:0, ease:Linear.easeNone,onComplete:removeFullImage,onCompleteParams:[fullImageContainer,textInfos]} );
			// ancienne méthode pour créer un Tween pour l'effet à la disparition de l'image -> il faudrait la paramétrer correctement donc nous préférerons la facilité : utiliser TweenLite !
			/*
			var myTween:Tween = new Tween( fullImageContainer, "scaleX", Linear.easeNone, 1,0, 2, true);
			var myTweenTwo:Tween = new Tween( 
										   fullImage,
										   "x",
										   Linear.easeNone,
										   0,
										   fullImage.width / 2,
										   2,
										   true);
			var myTweenThree:Tween = new Tween( 
											   fullImage,
											   "alpha",
											   Linear.easeNone,
											   1,
											   0,
											   1,
											   true);
			myTween.addEventListener( TweenEvent.MOTION_FINISH, removeFullImage );
			myTweenTwo.start( );
			myTweenThree.start( );
			myTween.start();
			*/
			
			var lng : uint = _imagesContainer.numChildren;
			
			// on fait réapparaître les vignettes
			// buttonMode fait également en sorte qu'elles se comportent comme des boutons (curseur de clic avec la souris)
			for ( var i : uint = 0 ; i < lng; i++ )
			{
				// on peut également utiliser TweenMax, la bibliothèque associée à TweenLite, pour effectuer des filtres sur les Tweens (ici, un filtre de flou)
				TweenMax.to(
							 	_imagesContainer.getChildAt( i ),
								GalleryConfig.THUMB_TWEEN_DURATION,
								{alpha:1, ease:Regular.easeOut, blurFilter:{blurX:0}} );
				Sprite( _imagesContainer.getChildAt( i ) ).buttonMode = true;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseEnabled = true;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseChildren = true;
				
				// meilleure alternative si on veut juste faire disparaitre/réapparaitre les vignettes
				/*
				_imagesContainer.getChildAt( i ).visible = true;
				Sprite( _imagesContainer.getChildAt( i ) ).buttonMode = true;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseEnabled = true;
				*/
				
				// autre alternative pour faire a moitié disparaitre puis completement réapparaitre les vignettes
				/*
				_imagesContainer.getChildAt( i ).alpha = 1;
				Sprite( _imagesContainer.getChildAt( i ) ).buttonMode = true;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseEnabled = true;
				Sprite( _imagesContainer.getChildAt( i ) ).mouseChildren = true;
				*/
			}
		}
		
		// méthode exécutée en cas d'échec au chargement du XML
		protected function chargeECHEC ( pEvt : XmlEvent ) : void
		{
			// on affiche dans la zone de notification un message d'erreur
			afficheNotif( "il y a eu une erreur au chargement du XML" );
		}
		
		// méthode utilitaire pour afficher directement un message dans la zone de notifications
		protected function afficheNotif ( str : String = '' ) : void
		{
			zoneDeNotif.tf.text = str;
			// ici, pour plus de rapidité, je récupère le formatage de texte inclus dans NotificationArea.as
			zoneDeNotif.tf.setTextFormat(zoneDeNotif.myFormat);
		}
	}
}